package org.argus.job.server.system.management.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.argus.common.core.enums.StatusEnum;
import org.argus.common.core.exception.ServiceException;
import org.argus.common.core.uuid.IdUtils;
import org.argus.common.web.core.data.ValueFieldsAssigner;
import org.argus.common.web.core.snowflake.SnowflakeIdGenerator;
import org.argus.job.server.system.allocate.AppAllocator;
import org.argus.job.server.system.domain.AppInfo;
import org.argus.job.server.system.management.mapper.AppInfoMapper;
import org.argus.job.server.system.management.model.AppInfoPageReq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author 刘文/Cliff Liu
 * @Date 2024/7/16
 */
@Service
@Slf4j
public class AppInfoService {

    @Autowired
    private AppInfoMapper mapper;

    @Autowired
    private SnowflakeIdGenerator idGenerator;

    @Autowired
    private ValueFieldsAssigner valueFieldsAssigner;

    @Autowired
    private AppAllocator appAllocator;

    public Page<AppInfo> page(Page page, AppInfoPageReq pageReq) {
        LambdaQueryWrapper<AppInfo> queryWrapper = Wrappers.lambdaQuery(AppInfo.class);
        if (StringUtils.isNotBlank(pageReq.getName())) {
            queryWrapper.like(AppInfo::getName, pageReq.getName());
        }
        if (StringUtils.isNotBlank(pageReq.getHost())) {
            queryWrapper.like(AppInfo::getHost, pageReq.getHost());
        }
        if (StringUtils.isNotBlank(pageReq.getDescription())) {
            queryWrapper.like(AppInfo::getDescription, pageReq.getDescription());
        }

        Page result = mapper.selectPage(page, queryWrapper);
        valueFieldsAssigner.assignValue(result.getRecords());
        return result;
    }

    public void add(AppInfo appInfo) {
        validate(appInfo);
        appInfo.setId(idGenerator.nextId());
        appInfo.setSecret(generateSecret());
        mapper.insert(appInfo);
        appAllocator.allocate();
        log.info("Add app {} succeed.", appInfo.getName());
    }

    private void validate(AppInfo appInfo) {
        int count = mapper.getDuplicateCount(appInfo);
        if (count > 0) {
            throw new ServiceException("已存在相同名称的应用");
        }
    }

    private String generateSecret() {
        return IdUtils.simpleUUID() + ":" + IdUtils.simpleUUID();
    }

    public void update(AppInfo appInfo) {
        validate(appInfo);
        mapper.updateAppInfo(appInfo);
    }

    public void updateStatus(AppInfo appInfo) {
        LambdaUpdateWrapper<AppInfo> updateWrapper = Wrappers.lambdaUpdate(AppInfo.class)
            .set(AppInfo::getStatus, appInfo.getStatus()).eq(AppInfo::getId, appInfo.getId());
        mapper.update(null, updateWrapper);
    }

    public void resetSecret(Long id) {
        LambdaQueryWrapper<AppInfo> queryWrapper = Wrappers.lambdaQuery(AppInfo.class).eq(AppInfo::getId, id);
        AppInfo appInfo = mapper.selectOne(queryWrapper);
        if (appInfo == null) {
            throw ServiceException.TARGET_NOT_EXIST;
        }
        // 上次密钥重置时间小于2天，不允许重置
        if (appInfo.getLastSecretChangeTime() != null
            && System.currentTimeMillis() - appInfo.getLastSecretChangeTime().getTime() < 2 * 24 * 60 * 60 * 1000L) {
            throw new ServiceException("密钥重置时间间隔小于两天，请两天后再重置");
        }
        
        String secret = generateSecret();
        LambdaUpdateWrapper<AppInfo> updateWrapper = Wrappers.lambdaUpdate(AppInfo.class)
            .set(AppInfo::getSecret, secret).set(AppInfo::getPreviousSecret, appInfo.getSecret())
            .set(AppInfo::getLastSecretChangeTime, new java.util.Date()).eq(AppInfo::getId, id);
        mapper.update(null, updateWrapper);
        log.info("Reset secret for app {} successfully.", id);
    }

    public List<AppInfo> list() {
        return mapper.getNormalApps();
    }

    public AppInfo findByAppName(String appName) {
        LambdaQueryWrapper<AppInfo> queryWrapper = Wrappers.lambdaQuery(AppInfo.class).eq(AppInfo::getName, appName).eq(AppInfo::getStatus, StatusEnum.NORMAL);
        return mapper.selectOne(queryWrapper);
    }
}
